數據的持久性就像是企業運營中的穩固倉庫,確保所有重要資產都能安全存放並隨時可取。Kubernetes 提供了兩個關鍵工具——PersistentVolume (PV) 和 PersistentVolumeClaim (PVC),來高效管理這些“數據倉庫”。
PV 就像是集群中的實際倉庫空間,是 Kubernetes 集群中的一個存儲資源,代表具體的存儲位置,如 NFS、GCE Persistent Disk、AWS EBS 等。PV 由集群管理員創建和管理,並且與 Pod 解耦,意味著它的生命週期獨立於任何單一 Pod。這樣,即使 Pod 被刪除或重新調度,PV 仍然保留其中的數據,確保數據的持久性和安全性。
PVC 類似於對倉庫空間的租用請求,描述了所需的存儲容量和訪問模式。具體來說,PVC 是 Pod 對 PV 的存儲需求申請,用戶可以根據應用的需求創建 PVC,例如申請特定大小的存儲空間(如 10Gi)以及訪問模式(如 ReadWriteOnce)。Kubernetes 會自動匹配符合 PVC 要求的 PV,並將兩者綁定在一起,實現存儲資源的動態分配和高效利用。
在 Kubernetes 中,PersistentVolume (PV) 和 PersistentVolumeClaim (PVC) 的設計旨在將存儲資源與 Pod 解耦,從而實現更靈活和高效的存儲管理。這種解耦帶來了以下主要優點:
與其他卷(如 emptyDir
、hostPath
)相比,PV 和 PVC 具有以下顯著優勢:
ReadWriteOnce
、ReadOnlyMany
、ReadWriteMany
),確保數據的安全性和共享需求。Retain
、Recycle
、Delete
),靈活管理 PVC 刪除後的 PV 行為,保障數據安全。透過這些優勢,PV 和 PVC 成為 Kubernetes 中管理持久性存儲的核心組件,為應用提供可靠的數據持久化支持,並提升整體系統的靈活性與效率。
以下是一個簡單的 PV 和 PVC 組態檔範例:
PersistentVolume (PV) 組態檔:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-example
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
nfs:
path: /exported/path/on/server
server: nfs-server.example.com
spec
:
capacity
:
storage: 10Gi
:指定 PV 的存儲容量為 10 GiB。accessModes
:
ReadWriteOnce
:該卷可以以讀寫方式掛載,但只能由一個節點掛載。persistentVolumeReclaimPolicy: Retain
:當使用該卷的 PersistentVolumeClaim (PVC) 被刪除時,PV 不會被自動刪除,而是會保留數據供後續使用。nfs
:使用 NFS 網絡文件系統作為存儲後端。
path: /exported/path/on/server
:指定 NFS 伺服器上導出的目錄路徑。server: nfs-server.example.com
:指定 NFS 伺服器的主機名或 IP 地址。PersistentVolumeClaim (PVC) 組態檔:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-example
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
accessModes
:
ReadWriteOnce
:PVC 請求一個可以被單個節點以讀寫模式掛載的 PersistentVolume。resources
:
requests
:
storage: 10Gi
:PVC 請求的存儲容量為 10GiB。當這個 PVC 被創建後,Kubernetes 會自動搜索一個符合要求的 PV(如上面的 pv-example
)並將其綁定到該 PVC。這樣,任何使用 pvc-example
的 Pod 都可以掛載到對應的 PV 上,並讀寫數據。
這樣的設計確保了應用程式數據的持久性和高可用性,即使 Pod 被重新調度或重新啟動,數據仍然可以被安全地保留和存取。
在這次練習中,我們會建立 hostPath 類型的 PersistentVolume。也就是使用節點上的檔案或目錄來模擬網路附加儲存。
注意:
在生產環境的叢集中,我們不會也不應該使用 hostPath,而是使用其他網路儲存資源作為 PersistentVolume 的來源,比如 Google Compute Engine 持久盤捲、NFS 共享卷或 Amazon Elastic Block Store 卷等等。
組態檔案: pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
kubectl apply -f pv.yaml
kubectl get pv task-pv-volume
---
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
task-pv-volume 10Gi RWO Retain Available manual <unset> 13s
輸出結果顯示該 PersistentVolume 的狀態(STATUS)
為 Available
。 這意味著它還沒有被繫結給 PersistentVolumeClaim。
我們要建立一個 PersistentVolumeClaim,它請求至少 3 GB 容量的卷, 該卷一次最多可以為一個節點提供讀寫訪問。
組態檔案: pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
kubectl apply -f pvc.yaml
建立 PersistentVolumeClaim 之後,Kubernetes 控制平面將尋找滿足申領要求的 PersistentVolume。 如果控制平面找到具有相同 StorageClass 的適當的 PersistentVolume, 則將 PersistentVolumeClaim 繫結到該 PersistentVolume 上。
kubectl get pv,pvc
---
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/task-pv-volume 10Gi RWO Retain Bound default/task-pv-claim manual <unset> 2m32s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/task-pv-claim Bound task-pv-volume 10Gi RWO manual <unset> 12s
PersistentVolume 的狀態 (STATUS) 變為了 Bound
,並且 PVC 的卷 (VOLUME) 也指向了那個 PersistentVolume。
組態檔案: pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: foo
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
---
apiVersion: v1
kind: Pod
metadata:
name: bar
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
這個組態檔案定義了兩個 Pod,它們分別名為 foo
和 bar
。兩個 Pod 使用了相同的 PersistentVolumeClaim
(PVC) 來掛載儲存卷,並在容器中使用該儲存卷,並將其掛載到容器內的 /usr/share/nginx/html
路徑上。這樣 foo
和 bar
都可以共同代理這個 PVC 內的文件。
kubectl get pod -o wide
---
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
bar 1/1 Running 0 12m 10.244.2.5 wslkind-worker2 <none> <none>
foo 1/1 Running 0 12m 10.244.2.6 wslkind-worker2 <none> <none>
可以看到,目前 Pod 都是建立在 wslkind-worker2
的節點上。
/mnt/data
路徑docker exec -it wslkind-worker2 /bin/sh
# ls /mnt/data
---
#
目前什麼檔案都沒有。
foo
Pod,並在 nginx 服務器根目錄建立一個 Html 網頁,裡面寫入系統時間kubectl exec -it foo -- /bin/sh
# echo `date` > /usr/share/nginx/html/index.html
foo
Pod 的 nginx 伺服器,檢查剛剛寫入的內容# curl 0.0.0.0
---
Tue Jul 23 09:40:50 UTC 2024
bar
Pod 中,訪問 nginx 伺服器$ kubectl exec -it bar -- /bin/sh
# curl 0.0.0.0
---
Tue Jul 23 09:50:34 UTC 2024
顯示出跟 foo
Pod 一樣的時間,代表剛剛在 foo
寫入的內容是存在的。
/mnt/data
路徑docker exec -it wslkind-worker2 /bin/sh
# cat /mnt/data/index.html
---
Tue Jul 23 09:50:34 UTC 2024
剛剛寫入的檔案也在這裡。
接下來我們要將 Pod 刪除重建,來驗證 PV 的內容是永久性的。
kubectl delete -f pod.yaml
---
pod "foo" deleted
pod "bar" deleted
kubectl apply -f pvc.yaml
---
persistentvolumeclaim/task-pv-claim created
kubectl get pv,pvc
---
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/task-pv-volume 10Gi RWO Retain Released default/task-pv-claim manual <unset> 103m
現在 PV 狀態變成 release
,代表它已經被解除 PVC 綁定,等待回收。
/mnt/data
路徑docker exec -it wslkind-worker2 /bin/sh
# cat /mnt/data/index.html
---
Tue Jul 23 09:50:34 UTC 2024
可以發現儘管我們刪除了 Pod 和 PVC,檔案依然保留在其中。
由於我們之前已經使用 PVC 申請了 PV,通常是需要回收才能再次使用。不過我們可以透過修改 PV 組態內容的 claimRef
欄位,重置 PV 的狀態。
kubectl patch pv task-pv-volume -p '{"spec":{"claimRef": null}}'
kubectl get pv
---
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
task-pv-volume 10Gi RWO Retain Available manual <unset> 105m
kubectl apply -f pvc.yaml
---
persistentvolumeclaim/task-pv-claim created
kubectl get pv,pvc
---
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/task-pv-volume 10Gi RWO Retain Bound default/task-pv-claim manual <unset> 106m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/task-pv-claim Bound task-pv-volume 10Gi RWO manual <unset> 2s
kubectl apply -f pod.yaml
---
pod/foo created
pod/bar created
foo
Pod 中,訪問 nginx 伺服器$ kubectl exec -it foo -- /bin/sh
# curl 0.0.0.0
---
Tue Jul 23 09:50:34 UTC 2024
PV 對象的創建,是由運維人員完成的。但是,在大規模的生產環境裡,這其實是一個非常麻煩的工作。這是因為,一個大規模的 Kubernetes 集群裡很可能有成千上萬個 PVC,這就意味著運維人員必須得事先創建出成千上萬個 PV。更麻煩的是,隨著新的 PVC 不斷被提交,運維人員就不得不繼續添加新的、能滿足條件的 PV,否則新的 Pod 就會因為 PVC 綁定不到 PV 而失敗。在實際操作中,這幾乎沒辦法靠人工做到。
為了進一步提升存儲管理的靈活性和自動化,Kubernetes 引入了 StorageClass。它定義了存儲的類型和配置,指定了如何動態地供應 PV。
StorageClass
可以設置存儲後端的具體屬性,如性能(IOPS)、冗餘(RAID)、備份策略等,以滿足不同應用的需求。簡單來說,StorageClass 對象的作用,其實就是創建 PV 的模板。用戶只需關注 PVC 的需求描述,StorageClass 便負責具體的存儲資源配置和管理,進一步簡化了存儲管理流程。
透過 PersistentVolume ,PersistentVolumeClaim 和 StorageClass ,Kubernetes 成功地將存儲資源與應用容器解耦,提供了靈活且高效的數據持久化管理方案。這不僅確保了應用數據在 Pod 重啟或重新調度後的持久性,還簡化了存儲資源的管理,使集群管理員能夠集中控制存儲資源,而用戶則專注於描述他們的存儲需求。